首页 > 试题广场 >

计算字符串的编辑距离

[编程题]计算字符串的编辑距离
  • 热度指数:133787 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解

Levenshtein 距离,又称编辑距离,指的是两个字符串之间,由一个转换成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。编辑距离的算法是首先由俄国科学家 Levenshtein 提出的,故又叫 Levenshtein Distance 。

例如:

字符串A: abcdefg

字符串B: abcdef

通过增加或是删掉字符 ”g” 的方式达到目的。这两种方案都需要一次操作。把这个操作所需要的次数定义为两个字符串的距离。

要求:

给定任意两个字符串,写出一个算法计算它们的编辑距离。

数据范围:给定的字符串长度满足




输入描述:

每组用例一共2行,为输入的两个字符串



输出描述:

每组用例输出一行,代表字符串的距离

示例1

输入

abcdefg
abcdef

输出

1
'''
会爆内存,易理解(递归 + 记忆化搜索)

思路:dfs(i, j)表示s1的前i个字符匹配s2的前j个字符最少需要几次操作。
考虑字符串s1,s2的第i位和第j位,有相同和不同两种情况。
如果相同,则匹配s1和s2的下一位,即dfs(i, j) = dfs(i + 1, j + 1)
如果不同,则有三种操作(s1固定,只对s2操作):
删除s2的当前位,匹配第j + 1位:dfs(i, j) -> dfs(i, j + 1) + 1
s2的当前位添加一个元素:dfs(i, j) -> dfs(i + 1, j) + 1
修改s2当前位元素:dfs(i, j) -> dfs(i + 1, j + 1) + 1
这三种操作取最小值,即dfs(i, j) = min(dfs(i + 1, j), dfs(i, j + 1), dfs(i + 1, j + 1)) + 1
中间涉及重复计算,因此采用记忆化搜索剪枝 from functools import lru_cache
s1 = input()
s2 = input()

@lru_cache(None)
def dfs(i, j):
    # 边界情况,如果其中一个匹配完了,剩余的都要删除
    if j == len(s2)&nbs***bsp;i == len(s1):
        return max(len(s1) - i, len(s2) - j)
    if s1[i] == s2[j]:
        return dfs(i + 1, j + 1)
    return min(dfs(i + 1, j), dfs(i, j + 1), dfs(i + 1, j + 1)) + 1
print(dfs(0, 0))
'''
# 手动定义cache数组 s1 = input()
s2 = input()
cache = [[-1 for _ in range(len(s2) + 1)] for __ in range(len(s1) + 1)]
def dfs(i, j):
    # 如果前面访问过了
    if cache[i][j] != -1:
        return cache[i][j] # 边界情况,如果其中一个匹配完了,剩余的都要删除     if j == len(s2) or i == len(s1):
        cache[i][j] = max(len(s1) - i, len(s2) - j)
        return cache[i][j]
    # s1第i位和s2第j位相同,无需操作,匹配下一位
    if s1[i] == s2[j]:
        cache[i][j] = dfs(i + 1, j + 1)
        return cache[i][j]
    # 否则,三种操作中选最小的
    cache[i][j] = min(dfs(i + 1, j), dfs(i, j + 1), dfs(i + 1, j + 1)) + 1
    return cache[i][j]
print(dfs(0, 0)) 

发表于 2023-09-17 00:46:10 回复(0)
我真傻,真的
发表于 2023-02-24 21:02:21 回复(0)
不懂动态规划的,建议先看一下动态规划,否则有些不好理解
n = input()  # apple 行
m = input()  # pooa  列
dp = [[x for x in range(len(n)+1)] for y in range(len(m)+1)]   # 先m后n
# 生成dp的最前方加一行和一列的空值
# 将两行list变为dp[i][j]的二维数组形式
# dp[i][j]表示以下标i-1为结尾的字符串str1,和以下标j-1为结尾的字符串str2,最近的编辑距离为dp[i][j]
for i in range(len(n)+1):
    dp[0][i] = i
for j in range(len(m)+1):
    dp[j][0] = j
for i in range(1, len(n)+1):  # 1:6
    for j in range(1, len(m)+1):  # 1:5
        if n[i-1] != m[j-1]:
            dp[j][i] = min(dp[j-1][i-1], dp[j][i-1], dp[j-1][i]) + 1
        else:
            dp[j][i] = dp[j-1][i-1]  # 这里注意i和j的位置
print(dp[-1][-1])


发表于 2022-09-08 17:31:20 回复(0)
为什么用最大字符串长度减去最长公共子序列长度不对呀?
a = input()
b = input()
m = len(a)
n = len(b)
mtx = [[0 for _ in range(n+1)] for _ in range(m+1)]
for i in range(1, m+1):
    for j in range(1, n+1):
        if a[i-1] == b[j-1]:
            mtx[i][j] = 1 + mtx[i-1][j-1]
        else:
            mtx[i][j] = max(mtx[i-1][j], mtx[i][j-1])
print(max(m,n)-mtx[-1][-1])


发表于 2022-08-26 14:19:59 回复(0)
def levenshtein(u, v):
    prev = None
    curr = [0] + list(range(1, len(v) + 1))
    for x in range(1, len(u) + 1):
        prev, curr = curr, [x] + ([None] * len(v))
        for y in range(1, len(v) + 1):
            delcost = prev[y] + 1
            addcost = curr[y - 1] + 1
            subcost = prev[y - 1] + int(u[x - 1] != v[y - 1])
            curr[y] = min(subcost, delcost, addcost)
    return curr[len(v)]

if __name__=="__main__":
    u = input()
    v = input()
    num = levenshtein(u,v)
    print(num)
发表于 2022-07-24 04:45:03 回复(0)
这道题是真不会啊,有没有大神过来讲解下啊
看排行里的答案都看不懂
发表于 2022-06-16 12:31:37 回复(0)
while 1:
    try:
        m,n=input(),input()
        long1,long2=len(m),len(n)
        dp=[[0 for i in range(long1+1)] for j in range(long2+1)]
        for i in range(long2+1):
            dp[i][0]=i
        for j in range(long1+1):
            dp[0][j]=j
        for i in range(1,long2+1):
            for j in range(1,long1+1):
                if m[j-1]==n[i-1]:
                    dp[i][j]=dp[i-1][j-1]
                else:
                    dp[i][j]=min(dp[i][j-1]+1,dp[i-1][j]+1,dp[i-1][j-1]+1)
        print(dp[-1][-1])
    except:
        break
                
            
            
        

发表于 2022-05-13 23:27:39 回复(0)
while True:
    try:
        s1, s2 = input(), input()
        n1, n2 = len(s1), len(s2)
        # 初始化数组dp,赋值
        dp = [[0] * (n2+1) for _ in range(n1+1)]
        
        # 初始化边界,赋值
        for i in range(1, n2+1):
            dp[0][i] = i
        for i in range(1, n1+1):
            dp[i][0] = i
            
        # 遍历两个字符串
        for i in range(1, n1+1):
            for j in range(1, n2+1):
                if s1[i-1] == s2[j-1]:
                    dp[i][j] = dp[i-1][j-1] #左上角元素
                else:
                    dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1 #左,上,左上,三个数中的最小值
        print(dp[n1][n2])
    except:
        break

发表于 2022-04-26 17:20:51 回复(0)
while True:
    try:
        str1 = input()
        str2 = input()
        m = len(str1)
        n = len(str2)
        dp = [[0] * (n+1) for _ in range(m+1)]
        for i in range(1, m+1):
            dp[i][0] = dp[i-1][0] + 1
        for j in range(1, n+1):
            dp[0][j] = dp[0][j-1] + 1
        # print(dp)
        for i in range(1, m+1):
            for j in range(1, n+1):
                if str1[i-1] == str2[j-1]:  # 相等 可以直接约掉
                    dp[i][j] = dp[i-1][j-1]  # 那么操作数就是对应的的操作数
                else:
                    # 对应三种操作,插入,删除,替换
                    dp[i][j] = min(dp[i-1][j] + 1, dp[i][j-1] + 1, dp[i-1][j-1]+1)
        # print(dp)
        print(dp[m][n])
        
    except:
        break

发表于 2022-04-12 15:16:32 回复(0)
str1=' '+input()
str2=' '+input()
len1=len(str1)
len2=len(str2)
f=[[0 for i in range(len2)] for j in range(len1)]
f[0][0]=0 
for i in range(1,len1):
    f[i][0]=i 
for j in range(1,len2):
    f[0][j]=j 
for i in range(1,len1):
    for j in range(1,len2):
        f[i][j]=min(f[i-1][j]+1,f[i][j-1]+1,f[i-1][j-1]+(0 if str1[i]==str2[j] else 1))
print(f[len1-1][len2-1])
                 
发表于 2022-04-10 22:44:10 回复(0)
## 动态规划
str1 = input()
str2 = input()
m = len(str1)
n = len(str2)
         
dp = [[1 for i in range(n+1)] for j in range(m+1)]#重点注意二维数据的创建方法,重点注意其横竖坐标,注意注意
for i in range(n+1):
    dp[0][i] = i
for j in range(m+1):
    dp[j][0] = j
    
for i in range(1,m+1):
    for j in range(1,n+1):
        if str1[i-1] == str2[j-1]:#如果当前两个字母相同,则跳过,步数不增加
            dp[i][j]=dp[i-1][j-1]
        else:  #如果两个字母不同,则有三种方式可以达成,删除、插入、替换,选择最小的前状态,步数加1
            dp[i][j] = min(dp[i-1][j-1],dp[i][j-1],dp[i-1][j])+1
print(dp[m][n])

发表于 2022-03-30 11:08:16 回复(0)
while True:
    try:
        a = input()
        b = input()
        la = len(a)
        lb = len(b)
        dp = [[0 for i in range(lb+1)] for j in range(la+1)]
        for i in range(la+1):
            dp[i][0] = i
        for j in range(lb+1):
            dp[0][j] = j
        for i in range(1, la+1):
            for j in range(1, lb+1):
                if a[i-1] == b[j-1]:
                    dp[i][j] = min(dp[i-1][j-1]-1, dp[i-1][j], dp[i][j-1])+1
                else:
                    dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1])+1
        print(dp[la][lb])
    except:
        break

发表于 2022-02-20 22:34:01 回复(0)

while True:
    try:
        strA,strB = input(),input()
        la,lb = len(strA)+1,len(strB)+1
        dp = [[i for i in range(lb)] for j in range(2)]
        for i in range(1,la):
            dp[1][0]=i        
            for j in range(1,lb):
                dp[1][j] = min(dp[0][j-1]+(1 if strA[i-1]!=strB[j-1] else 0),dp[1][j-1]+1,dp[0][j]+1)
            dp[0]=dp[1][:]
        print(dp[0][-1])
    except:
        break

发表于 2021-11-24 05:55:27 回复(0)
'''
递归
- 大量的重复计算,导致超时
- 优化:Memorization(二维数组), 动态规划
'''


def distance(a, b):
    if len(a) == 0:
        return len(b)
    if len(b) == 0:
        return len(a)

    cos = 0
    if a[-1] != b[-1]:
        cos = 1

    lev1 = distance(a[:len(a) - 1], b) + 1
    lev2 = distance(a, b[:len(b) - 1]) + 1
    lev3 = distance(a[:len(a) - 1], b[:len(b) - 1]) + cos

    return min(lev1, lev2, lev3)

'''
动态规划:
 - 自底向上,避免大量重复运算
 - 从状态转移抽象出状态转移方程
   1. 如果StringA[i] == StringB[j], 那么dp[i][j] = dp[i-1][j-1]
   2. 否则,dp[i][j] == min{dp[i-1][j], dp[i][j-1], dp[i-1][j-1]} + 1, 对应于对String B执行insert, delete, replace操作
 - 测试
   1. 字符串为空(边界)
   2. 正常字符串
'''


def dynamic(a, b):

    dp = [[0 for j in range(len(b) + 1)] for i in range(len(a) + 1)]

    # 构造初始解,即某字符串为空的情况
    for i in range(1, len(a) + 1):
        dp[i][0] = i
    for j in range(1, len(b) + 1):
        dp[0][j] = j

    # 状态转移方程,注意边界!
    for i in range(len(a)):
        for j in range(len(b)):
            if a[i] == b[j]:
                dp[i + 1][j + 1] = dp[i][j]
            else:
                dp[i + 1][j + 1] = min(dp[i][j + 1], dp[i + 1][j], dp[i][j]) + 1

    return dp[-1][-1]


if __name__ == '__main__':
    while True:
        try:
            a = input()
            b = input()
            res = dynamic(a, b)
            # res = distance(a, b)
            print(res)
        except:
            break

发表于 2021-11-23 11:13:46 回复(0)
while True:
    try:
        s1, s2 = input(), input()
        s1 = list(s1)
        s2 = list(s2)
        s1.insert(0, "")
        s2.insert(0, "")
        lens1 = len(s1)
        lens2 = len(s2)
        dp = []
        for y in range(lens2):
            s = []
            for x in range(lens1):
                if y == 0:
                    s.append(x)
                else:
                    s.append(0 * x)
            dp.append(s)
        # 初始化边界值
        for y in range(lens2):
            dp[y][0] = y
#         print(dp)

        dp[0][0] = 0
        for y in range(1, lens2):
            ss2 = s2[y]
            for x in range(1, lens1):
                ss1 = s1[x]
                if ss1 == ss2:
                    dp[y][x] = dp[y - 1][x - 1]
                else:
                    dp[y][x] = min(dp[y - 1][x - 1], dp[y - 1][x], dp[y][x - 1]) + 1
        print(dp[-1][-1])
    except:
        break
发表于 2021-11-13 22:57:44 回复(0)
递归,测试通过,但是提交结果运行超时!
while True:
    try:
        def Levenshtein(a: str, b: str):
            if len(a) <= 1:
                 return len(b)-1
            if len(b) <= 1:
                 return len(a)-1
            if a[len(a)-1] == b[len(b)-1]:
                a = a[0:len(a)-1]
                b = b[0:len(b)-1]
                return Levenshtein(a,b)
            else:
                a1 = a[0:len(a)-1]
                b1 = b[0:len(b)-1]
                return min((Levenshtein(a1,b)+1),(Levenshtein(a,b1)+1),(Levenshtein(a1,b1)+1))
        s1 = '#' + input()
        s2 = '#' + input()
        x = Levenshtein(s1,s2)
        print(x)
    except:
        break
发表于 2021-09-11 16:29:09 回复(0)
动态规划
抓住状态:两个序列的最后一个字符是否相同
递推式:若相同则取为两个序列前一个字符处的“字符串距离”(lev[i-1][j-1]),若不同则需要判断字符串1加一个、字符串2加一个、字符串末尾换一个,这三种情况那个更小,取更小的加一
while True:
    try:
        input1=input();length1=len(input1)#行数
        input2=input();length2=len(input2)#列数
        lev=[[0]*(length2+1) for _ in range(length1+1)]
        
        for k in range(1,length2+1):
            lev[1][k]=k-1 if input1[0]==input2[k-1] else k
        for k in range(1,length1+1):
            lev[k][1]=k-1 if input2[0]==input1[k-1] else k
            
        i=2
        while i<=length1:
            j=2
            while j<=length2:
                if input1[i-1]==input2[j-1]:
                    lev[i][j]=lev[i-1][j-1]
                else:
                    lev[i][j]=min(lev[i-1][j-1]+1,lev[i][j-1]+1,lev[i-1][j]+1)
                j+=1
            i+=1
        
        print(lev[i-1][j-1])
    except:
        break


发表于 2021-08-17 11:15:57 回复(0)